/**
 * Copyright (C) @2014 Webank Group Holding Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.webank.framework.config.context.support;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;

import cn.webank.framework.config.integration.sao.ConfigSao;
import cn.webank.framework.config.integration.sao.impl.ConfigPojoSao;
import cn.webank.framework.dto.ConfigDTO;
import cn.webank.framework.exception.SysException;
import cn.webank.rmb.common.Global;

/**
 * 配置中心信息读取类
 * 
 * @author jonyang
 * 
 *         &lt;bean class=
 *         "cn.webank.framework.config.context.support.WeBankRemoteSourcesPlaceholderConfigurer"
 *         &gt; &lt;property name="fileEncoding"&gt;
 *         &lt;value&gt;utf-8&lt;/value&gt; &lt;/property&gt; &lt;property
 *         name="sysId"&gt; &lt;value&gt;rcs-otp&lt;/value&gt; &lt;/property&gt;
 *         &lt;property name="itemId"&gt;
 *         &lt;value&gt;start-config&lt;/value&gt; &lt;/property&gt;
 *         &lt;property name="configSao"&gt; &lt;ref bean="configSao" /&gt;
 *         &lt;/property&gt; &lt;/bean&gt; &lt;roperty name="location"&gt;
 *         &lt;value&gt;classpath:rcs-otp.properties&lt;/value&gt;
 *         &lt;/property&gt;
 * 
 *
 */

public class WeBankRemoteSourcesPlaceholderConfigurer extends
		PropertySourcesPlaceholderConfigurer implements InitializingBean {

	private final static Logger LOG = LoggerFactory
			.getLogger(WeBankRemoteSourcesPlaceholderConfigurer.class);

	private String fileEncoding = "UTF-8";

	private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();

	private String sysId;

	private String itemId;

	private String dcnNo;

	private String configServerHost;

	private String configServerPort;

	private String adminDcnNo;

	private ConfigSao configSao;

	public WeBankRemoteSourcesPlaceholderConfigurer() {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		this.configSao = new ConfigPojoSao();
		this.configSao.setHost(this.configServerHost);
		this.configSao.setPort(this.configServerPort);
		this.configSao.setAdminDcnNo(this.adminDcnNo);

	}

	/**
	 * @return the fileEncoding
	 */
	public String getFileEncoding() {
		return fileEncoding;
	}

	/**
	 * @param fileEncoding
	 *            the fileEncoding to set
	 */
	public void setFileEncoding(String fileEncoding) {
		this.fileEncoding = fileEncoding;
		super.setFileEncoding(fileEncoding);
	}

	/**
	 * Load properties into the given instance.
	 * 
	 * @param props
	 *            the Properties instance to load into
	 * @throws IOException
	 *             in case of I/O errors
	 * @see #setLocations
	 */
	public void loadProperties(Properties props) throws IOException {

		super.loadProperties(props);

		// 防止RMBUtil从配置文件读取
		Global onlyInstance = Global.onlyInstance();
		System.setProperty("systemId",//onlyInstance.getSystemIdKey(),
				props.getProperty("rmb.client.system.id"));
		System.setProperty("systemDCN",//onlyInstance.getSystemDCNKey(),
				props.getProperty("rmb.client.system.dcn"));

		// 从CMC-CC获取启动配置文件
		if (StringUtils.hasLength(this.sysId) && this.sysId.startsWith("${")
				&& this.sysId.endsWith("}")) {
			this.sysId = props.getProperty(
					this.sysId.substring(2, this.sysId.length() - 1),
					this.sysId);
		}

		if (StringUtils.hasLength(this.dcnNo) && this.dcnNo.startsWith("${")
				&& this.dcnNo.endsWith("}")) {
			this.dcnNo = props.getProperty(
					this.dcnNo.substring(2, this.dcnNo.length() - 1),
					this.dcnNo);
		}

		if (StringUtils.hasLength(this.itemId) && this.itemId.startsWith("${")
				&& this.itemId.endsWith("}")) {
			this.itemId = props.getProperty(
					this.itemId.substring(2, this.itemId.length() - 1),
					this.itemId);
		}

		// get from config center
		if (StringUtils.hasLength(this.sysId)
				&& StringUtils.hasLength(this.itemId)
				&& StringUtils.hasLength(this.dcnNo) && this.configSao != null) {
			ConfigDTO config = this.configSao.getConfig(this.sysId, this.dcnNo,
					this.itemId);

			if (config != null && config.getContent() != null) {
				String content = config.getContent();
				InputStream stream = null;
				try {
					// load to properties
					stream = new ByteArrayInputStream(
							content.getBytes(fileEncoding));

					this.propertiesPersister.load(props, stream);

				} finally {
					if (stream != null) {
						stream.close();
					}
				}

			} else {
				LOG.error("get config from remote error.");
			}
		}

		// rmb util properties modify
		// Global.java Constants.java RMBUtil.java
		String[] names = new String[] { "clientVersion", "solIp", "solPort",
				"solPwd", "solVpn", "solReapplySubscriptions",
				"solSSLValidateCertification", "flowsSopWaitTime",
				"pubWindowSize", "threadPoolCoreSize", "threadPoolMaxSize",
				"threadPoolThreadKeepAliveTime", "threadPoolQueueSize" };

		String[] keys = new String[] { "rmb.client.version",
				"rmb.client.sol.ip", "rmb.client.sol.port",
				"rmb.client.sol.pwd", "rmb.client.sol.vpn",
				"rmb.client.sol.reapplySubscriptions",
				"rmb.client.sol.sslValidateCertificate",
				"rmb.client.flow.stop.waittime",
				"rmb.client.sol.pub.window.size",
				"rmb.threadPool.thread.count",
				"rmb.threadPool.thread.maxCount",
				"rmb.threadPool.thread.keepAliveTime",
				"rmb.threadPool.queueSize" };

		try {
			for (int i = 0; i < names.length; i++) {
				Field field = Global.class.getDeclaredField(names[i]);
				boolean oldAccessible = field.isAccessible();
				field.setAccessible(true);
				if (StringUtils.hasLength((String) props.get(keys[i]))) {
					if (field.get(onlyInstance) instanceof String) {
						field.set(onlyInstance, props.get(keys[i]));
					} else if (field.get(onlyInstance) instanceof Boolean) {
						field.set(onlyInstance, Boolean
								.parseBoolean((String) props.get(keys[i])));
					} else if (field.get(onlyInstance) instanceof Boolean) {
						field.set(onlyInstance,
								Integer.parseInt((String) props.get(keys[i])));
					}
				}
				field.setAccessible(oldAccessible);
			}

		} catch (NoSuchFieldException | SecurityException
				| IllegalArgumentException | IllegalAccessException e) {
			throw new SysException("modify rmb field exception", e);
		}

	}

	/**
	 * @return the sysId
	 */
	public String getSysId() {
		return sysId;
	}

	/**
	 * @param sysId
	 *            the sysId to set
	 */
	public void setSysId(String sysId) {
		this.sysId = sysId;
	}

	/**
	 * @return the itemId
	 */
	public String getItemId() {
		return itemId;
	}

	/**
	 * @param itemId
	 *            the itemId to set
	 */
	public void setItemId(String itemId) {
		this.itemId = itemId;
	}

	/**
	 * @return the configSao
	 */
	public ConfigSao getConfigSao() {
		return configSao;
	}

	/**
	 * @param configSao
	 *            the configSao to set
	 */
	public void setConfigSao(ConfigSao configSao) {
		this.configSao = configSao;
	}

	/**
	 * @return the propertiesPersister
	 */
	public PropertiesPersister getPropertiesPersister() {
		return propertiesPersister;
	}

	/**
	 * @param propertiesPersister
	 *            the propertiesPersister to set
	 */
	public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
		this.propertiesPersister = propertiesPersister;
		super.setPropertiesPersister(propertiesPersister);
	}

	/**
	 * @return the dcnNo
	 */
	public String getDcnNo() {
		return dcnNo;
	}

	/**
	 * @param dcnNo
	 *            the dcnNo to set
	 */
	public void setDcnNo(String dcnNo) {
		this.dcnNo = dcnNo;
	}

	/**
	 * @return the configServerHost
	 */
	public String getConfigServerHost() {
		return configServerHost;
	}

	/**
	 * @param configServerHost
	 *            the configServerHost to set
	 */
	public void setConfigServerHost(String configServerHost) {
		this.configServerHost = configServerHost;
	}

	/**
	 * @return the configServerPort
	 */
	public String getConfigServerPort() {
		return configServerPort;
	}

	/**
	 * @param configServerPort
	 *            the configServerPort to set
	 */
	public void setConfigServerPort(String configServerPort) {
		this.configServerPort = configServerPort;
	}

	/**
	 * @return the adminDcnNo
	 */
	public String getAdminDcnNo() {
		return adminDcnNo;
	}

	/**
	 * @param adminDcnNo
	 *            the adminDcnNo to set
	 */
	public void setAdminDcnNo(String adminDcnNo) {
		this.adminDcnNo = adminDcnNo;
	}
}
